﻿/**
 * basic usefull methods
 * @class basic
 * @static
 */
var basic = {
	/**
	 * returns true if it is a DOM element
	 * @method isElement
	 * @param
	 * @return boolean
	 */
	isElement: function( o ) {
		return( 
			typeof HTMLElement === 'object' ? 
				o instanceof HTMLElement :
    			typeof o === 'object' && o.nodeType === 1 && typeof o.nodeName === 'string'
		);
	},
	/**
	 * remove all element's children
	 * @method 
	 * @param htmlElement
	 */
	clearElement: function( el ) {
		while( el.firstChild ) {
			el.removeChild( el.firstChild );
		}	
	}
};

/**
 * @class GMapsDD
 * @constructor
 * @param htmlElement | string el
 * @param GLatLng coords
 * @parma int zoom
 * 
 * @todo serialize 
 * @todo load
 */
function GMapsDD( el, coords, zoom ) {
	if( arguments.length == 3 || arguments.length == 2 ) {
		if( YAHOO.lang.isString( el ) ) {
			el = YAHOO.util.Dom.get( el );
		}
		if( basic.isElement( el ) ) {
			this.el_ = el;
			if( arguments.length == 3 ) {
				this.init_( coords, zoom );
			}
			else {
				this.load_( el, coords );
			}
		}
		else {
			throw new Error( 'First parameter must be a DOM element or correct element\'s ID.' );
		}
	}
	else {
		throw new Error( 'Wrong number of parameters' );
	}
};

(function() {

	var lang = {
		controls			: 'kontrolki',
		scale				: 'skala',
		mapType			: 'typ mapy',
		addressBar		: 'pasek adresu',
		overview			: 'podgląd',
		smallZoom		: 'powiększenie małe',
		smallZoom3d 	: 'powiększenie małe 3D',
		smallControl  	: 'powiększenie małe + przesuwanie małe',
		largeControl		: 'powiększenie duże + przesuwanie duże',
		largeControl3d	: 'powiększenie duże 3D + przesuwanie duże 3D' 
	};

	GMapsDD.prototype = {
		/**
		 * serialize widget in JSON
		 * @method serialize
		 */
		serialize: function() {
			var control = null;
			if( this.control_ instanceof  GLargeMapControl3D ) { control = 'large3d' };
			if( this.control_ instanceof  GLargeMapControl ) { control = 'large' };
			if( this.control_ instanceof  GSmallMapControl ) { control = 'small' };
			if( this.control_ instanceof  GSmallZoomControl3D) { control = 'smallZoom3d' };
			if( this.control_ instanceof  GSmallZoomControl  ) { control = 'smallZoom' };
			
			return YAHOO.lang.JSON.stringify({
				zoom				: this.map_.getZoom(),
				coords				: [ this.map_.getCenter().lat(), this.map_.getCenter().lng() ],
				control				: control,
				typeControl		: this.typeControl_ ? true : false,
				scaleControl		: this.scaleControl_ ? true : false,
				overviewControl	: this.overviewControl_ ? true : false,
				navControl			: this.navControl_ ? true : false,
				position				: {
					x			: YAHOO.util.Dom.getStyle( this.el_, 'left' ),
					y			: YAHOO.util.Dom.getStyle( this.el_, 'top' )
				},
				dimensions		: {
					width	: YAHOO.util.Dom.getStyle( this.el_, 'width' ),
					height	: YAHOO.util.Dom.getStyle( this.el_, 'height' )
				}
			});
		},
		/**
		 * loads and initalize object from JSON
		 * @method load
		 * @method string data
		 */
		load_: function( el, data ) {
			var obj = data;
			if( YAHOO.lang.isString( data ) ) {
				var obj = YAHOO.lang.JSON.parse( data );
			}
			
			if( YAHOO.lang.isObject( obj ) ) {
				this.init_( new GLatLng( obj.coords[ 0 ], obj.coords[ 1 ] ), obj.zoom  );
				
				switch( obj.control ) {
					case 'large3d':
						this.addMapControl( new GLargeMapControl3D(), 0 );
						break;
					case 'large':
						this.addMapControl( new GLargeMapControl(), 1 );
						break;
					case 'small':
						this.addMapControl( new GSmallMapControl(), 2);
						break;
					case 'smallZoom3d':
						this.addMapControl( new GSmallZoomControl3D(), 3 );
						break;
					case 'smallZoom':
						this.addMapControl( new GSmallZoomControl(), 4 );
						break;
					default:
						break;
				}
				if( obj.typeControl ) {
					this.addTypeControl();
				}
				if( obj.scaleControl ) {
					this.addScaleControl();
				}
				if( obj.overviewControl ) {
					this.addOverviewControl();
				}
				if( obj.navControl ) {
					this.addNavControl();
				}
				
			}
			else {
				throw new Error( 'Data object must be a string or object' );
			}
		},
		/**
		 * add particular map's control
		 * @method addMapControl
		 * @param GControl control
		 * @param int index
		 */
		addMapControl: function( control, index ) {
		
			if( index == this.controlIndex_ ) { 
				this.map_.removeControl( this.control_ );
				this.contextMenuSetProperty( [ 0, index ], 'checked', false );
				this.controlIndex_ = null;
				return;
			}
			
			this.contextMenuSetProperty( [ 0, index ], 'checked', true );
			
			if( YAHOO.lang.isNumber( this.controlIndex_ ) ) {
				this.contextMenuSetProperty( [ 0, this.controlIndex_ ], 'checked', false );
			}
			
			this.map_.removeControl( this.control_ );  // remove old control
			this.control_ = control;
			this.controlIndex_ = index;
			this.map_.addControl( this.control_ );
			a = this.control_;
		},
		/**
		 * add map's types control
		 * @method addTypeControl
		 */
		addTypeControl: function() {
			this.typeControl_ = new GMapTypeControl();
			this.map_.addControl( this.typeControl_ );
			this.contextMenuSetProperty( 2, 'checked', true );
			this.contextMenuSetProperty( 2, 'onclick', {
				fn			: function() {
					this.removeTypeControl();
				},
				scope   : this
			});
		},
		/**
		 * remove map's types control
		 * @method removeTypeControl
		 */
		removeTypeControl: function() {
			this.map_.removeControl( this.typeControl_ );
			this.typeControl_ = null;
			this.contextMenuSetProperty( 2, 'checked', false );
			this.contextMenuSetProperty( 2, 'onclick', {
				fn			: function() {
					this.addTypeControl();
				},
				scope   : this
			});
		},
		/**
		 * add map's scale control
		 * @method addScaleControl
		 */
		addScaleControl: function() {
			this.scaleControl_ = new GScaleControl();
			this.map_.addControl( this.scaleControl_ );
			this.contextMenuSetProperty( 1, 'checked', true );
			this.contextMenuSetProperty( 1, 'onclick', {
				fn			: function() {
					this.removeScaleControl();
				},
				scope   : this
			});
		},
		/**
		 * remove map's scale control
		 * @method removeScaleControl
		 */
		removeScaleControl: function() {
			this.map_.removeControl( this.scaleControl_ );
			this.scaleControl_ = null;
			this.contextMenuSetProperty( 1, 'checked', false );
			this.contextMenuSetProperty( 1, 'onclick', {
				fn			: function() {
					this.addScaleControl();
				},
				scope   : this
			});
		},
		/**
		 * add a collapsible overview map in the corner of the screen
		 * @method addOverviewControl
		 */
		addOverviewControl: function() {
			try {
				this.overviewControl_ = new GOverviewMapControl();
				this.map_.addControl( this.overviewControl_ );
				this.contextMenuSetProperty( 3, 'checked', true );
				this.contextMenuSetProperty( 3, 'onclick', {
					fn			: function() {
						this.removeOverviewControl();
					},
					scope   : this
				});
			} catch( e ) {
				console.debug( 'addOverviewControl', e );
			}
		},
		/**
		 * remove overview control
		 * @method removeOverviewControl
		 */
		removeOverviewControl: function() {
			try {
				this.map_.removeControl( this.overviewControl_ );
				this.overviewControl_ = null;
				this.contextMenu_.getItem( 3 ).cfg.setProperty( 'checked', false );
				this.contextMenu_.getItem( 3 ).cfg.setProperty( 'onclick', {
					fn			: function() {
						this.addOverviewControl();
					},
					scope   : this
				});
			} catch( e ) {
				console.debug( 'removeOverviewControl', e );
			}
		},
		/**
		 * a dynamic label indicating the "address" of the current viewport, appropriate to zoom level
		 * @method addNavControl
		 */
		addNavControl: function() {
			this.navControl_ = new GNavLabelControl();
			this.map_.addControl( this.navControl_ );
			this.contextMenuSetProperty( 4, 'checked', true );
			this.contextMenuSetProperty( 4, 'onclick', {
				fn			: function() {
					this.removeNavControl();
				},
				scope   : this
			});
		},
		/**
		 * remove "address" control
		 * @method removeNavControl
		 */
		removeNavControl: function() {
			this.map_.removeControl( this.navControl_ );
			this.navControl_ = null;
			this.contextMenuSetProperty( 4, 'checked', false );
			this.contextMenuSetProperty( 4, 'onclick', {
				fn			: function() {
					this.addNavControl();
				},
				scope   : this
			});
		},
		/**
		 * set menu item's property 
		 * @method contextMenuSetProperty
		 * @param array | number
		 * @param string
		 * @param 
		 */
		contextMenuSetProperty: function( coords, property, value ) {
			if( YAHOO.lang.isNumber( coords ) ) {
				this.contextMenu_.getItem( coords ).cfg.setProperty( property, value );
			}
			else {
				if( YAHOO.lang.isArray( coords ) ) { 
					var tmp = this.contextMenu_.getItem( coords[ 0 ] );
					for( var i = 1; i < coords.length; i++ ) {
						tmp = tmp.cfg.getProperty( 'submenu' ).getItem( coords[ i ] );
					}
					tmp.cfg.setProperty( property, value );
				}
				else {
					throw new Error( 'Coordinates to menu item must be a number or array' );
				}
			}
		},
		/**
		 * context menu initialization
		 * @method contextMenuInit_
		 */
		contextMenuInit_: function() {
			var cm =  [ 
				{
					text	: lang.controls,
					submenu : {
						id 			: this.el_.id + 'contextmen-controls',
						itemdata	: [
							{
								text: lang.largeControl3d,
								onclick: {
									fn: function(){
										this.addMapControl( new GLargeMapControl3D(), 0 );
									},
									scope: this
								}
							},
							{
								text: lang.largeControl,
								onclick: {
									fn: function(){
										this.addMapControl( new GLargeMapControl(), 1 );
									},
									scope: this
								}
							},
							{
								text: lang.smallControl,
								onclick: {
									fn: function(){
										this.addMapControl( new GSmallMapControl(), 2);
									},
									scope: this
								}
							},
							{
								text: lang.smallZoom3d,
								onclick: {
									fn: function(){
										this.addMapControl( new GSmallZoomControl3D(), 3 );
									},
									scope: this
								}
							},
							{
								text: lang.smallZoom,
								onclick: {
									fn: function(){
										this.addMapControl( new GSmallZoomControl(), 4 );
									},
									scope: this
								}
							}
						]
					}
				},
				{
					text	: lang.scale,
					onclick : {
						fn		: function() { 
							this.addScaleControl(); 
						}, 
						scope   : this
					}
				},
				{
					text	: lang.mapType,
					onclick : {
						fn		: function() {
							this.addTypeControl();
						},
						scope   : this
					}
				},
				{
					text	: lang.overview,
					onclick : {
						fn		: function() {
							this.addOverviewControl();
						},
						scope   : this
					}
				},
				{
					text	: lang.addressBar,
					onclick : {
						fn		: function() {
							this.addNavControl();
						},
						scope   : this
					}
				}
			];
			this.contextMenu_ = new YAHOO.widget.ContextMenu(
					this.el_.id + 'contextmenu', 
					{
							trigger: this.el_.id,
							itemdata: cm
					}
			);
			this.contextMenu_.render( document.body );
		},
		/**
		 * draggable interface initialization
		 * @method ddInit_
		 * @private
		 */
		ddInit_: function() {
			this.ddKnobInit_();
			dd = new YAHOO.util.DD( this.el_ ); 
			dd.setOuterHandleElId( this.ddKnob_.id ); 
		},
		/**
		 * drag&drop hanlder initialization
		 * @method ddKnobInit_
		 */
		ddKnobInit_: function() {
			this.ddKnob_ = document.createElement( 'div' );
			this.ddKnob_.id = this.el_.id + '-ddknob';
			this.el_.appendChild( this.ddKnob_ );
			YAHOO.util.Dom.addClass( this.ddKnob_, 'ddknob' );
		},
		/**
		 * show d&d handler knob
		 * @method showDDKnob
		 */
		showDDKnob: function() {
			if( this.hideTimeout_ ) {
				clearTimeout( this.hideTimeout_ );
			}
			YAHOO.util.Dom.setStyle( this.ddKnob_, 'display', 'block' );
		},
		/**
		 * hide d&d handler knob
		 * @method hideDDKnob
		 */
		hideDDKnob: function() {
			var that = this;
			if( this.hideTimeout_ ) {
				clearTimeout( this.hideTimeout_ );
			}
			this.hideTimeout_ = setTimeout( function() {
				YAHOO.util.Dom.setStyle( that.ddKnob_, 'display', 'none' );
			}, 1000 * 1 );
		},
		/**
		 * basic events initialization
		 * @method basicEventsInit_
		 * @private
		 */
		basicEventsInit_: function() {
			YAHOO.util.Event.removeListener( this.el_, 'mouseover' );
			YAHOO.util.Event.removeListener( this.el_, 'mouseout' );
			YAHOO.util.Event.on( this.el_, 'mouseover', function() {
				this.showDDKnob();
			}, this, true );
			
			YAHOO.util.Event.on( this.el_, 'mouseout', function() {
				this.hideDDKnob();
			}, this, true );
			
			var that = this;
			GEvent.addListener( this.map_, 'click', function() {
				that.contextMenu_.hide();
			});
			
			GEvent.addListener( this.map_, 'dragstart', function() {
				that.contextMenu_.hide();
			});
		},
		coreInit_: function() {
				basic.clearElement( this.el_ ); // remove all element's children
				this.map_ = new GMap2( this.el_ );
				this.ddInit_();
				this.contextMenuInit_();
				this.basicEventsInit_();
		},
		/**
		 * initialization
		 * @method init_
		 * @static
		 */
		init_: function( coords, zoom ) {
			if ( GBrowserIsCompatible() ) {
				this.coreInit_();
				this.map_.setCenter( coords, zoom );
			}
			else {
				throw new Error( 'Google maps API can\'t be used in the current browser.' );
			}
		}
	};
})();